from typing import Dict, Any, List
import logging
from protocol.codec import write_uint, to_hex
from protocol.types import DataType
from protocol.frames.base import encode_time_bcd, pad_bytes_ff, write_len_by_prefix
from protocol.common import _err, _ensure_int, _evaluate_condition, _apply_field_modifications


def _encode_number(value: Any, size: int, path: List[str]) -> bytes:
    iv = _ensure_int(value, path)
    maxv = (1 << (size * 8)) - 1
    if iv < 0 or iv > maxv:
        raise _err(path, f"数值越界(0..{maxv}): {iv}")
    return write_uint(iv, size)


def _encode_string(cfg: Dict[str, Any], path: List[str]) -> bytes:
    charset = cfg.get('charset', 'gbk')
    val = cfg.get('value', '')
    if isinstance(val, bytes):
        raw = val
    else:
        try:
            raw = str(val).encode(charset)
        except Exception as e:
            raise _err(path, f"字符串编码失败(charset={charset}): {e}")
    fixed_len = cfg.get('fixedLen')
    len_prefix = cfg.get('lenPrefix')
    remainder = cfg.get('remainder', False)  # 是否解析剩余字节，默认为false
    
    if fixed_len is not None:
        fl = int(fixed_len)
        if len(raw) > fl:
            raise _err(path, f"STRING 超长(expected={fl}, actual={len(raw)})")
        return raw + b"\x00" * (fl - len(raw))
    if len_prefix is not None:
        lp = str(len_prefix)
        return write_len_by_prefix(lp, len(raw)) + raw
    if remainder:
        # 剩余字节模式：直接返回原始字节，不添加长度前缀
        return raw
    # 若三者均未声明，报错
    raise _err(path, "STRING 需要 fixedLen、lenPrefix 或 remainder")




def _evaluate_condition_expression(value: Any, condition: str) -> bool:
    """解析条件表达式，支持多种计数策略"""
    try:
        value = int(value)
        condition = condition.strip()
        
        # 精确匹配: == 0, == 1, == 2
        if condition.startswith('== '):
            target = int(condition[3:])
            return value == target
        
        # 不等于匹配: != 0, != 1
        elif condition.startswith('!= '):
            target = int(condition[3:])
            return value != target
        
        # 大于等于匹配: >= 0, >= 1
        elif condition.startswith('>= '):
            target = int(condition[3:])
            return value >= target
        
        # 小于等于匹配: <= 2, <= 5
        elif condition.startswith('<= '):
            target = int(condition[3:])
            return value <= target
        
        # 大于匹配: > 0, > 1
        elif condition.startswith('> '):
            target = int(condition[2:])
            return value > target
        
        # 小于匹配: < 2, < 5
        elif condition.startswith('< '):
            target = int(condition[2:])
            return value < target
        
        # 多值匹配: in [0,1], in [2,3,4]
        elif condition.startswith('in ['):
            # 解析 [0,1,2] 格式
            values_str = condition[4:-1]  # 去掉 'in [' 和 ']'
            if values_str.strip():
                values = [int(x.strip()) for x in values_str.split(',')]
                return value in values
            else:
                return False
        
        # 范围匹配: range [0,2], range [1,5]
        elif condition.startswith('range ['):
            # 解析 [0,2] 格式
            range_str = condition[7:-1]  # 去掉 'range [' 和 ']'
            parts = range_str.split(',')
            if len(parts) == 2:
                start = int(parts[0].strip())
                end = int(parts[1].strip())
                return start <= value <= end
        
        # 逻辑或匹配: == 0 || == 1
        elif ' || ' in condition:
            conditions = condition.split(' || ')
            return any(_evaluate_condition_expression(value, cond.strip()) for cond in conditions)
        
        # 逻辑与匹配: >= 0 && <= 2
        elif ' && ' in condition:
            conditions = condition.split(' && ')
            return all(_evaluate_condition_expression(value, cond.strip()) for cond in conditions)
        
        else:
            # 默认按精确匹配处理
            return value == int(condition)
            
    except Exception as e:
        raise ValueError(f'条件表达式解析失败: {condition}, 错误: {e}')


def _encode_bytes(cfg: Dict[str, Any], path: List[str]) -> bytes:
    v = cfg.get('value')
    ascii_mode = cfg.get('ascii', False)  # 默认按HEX解析
    fixed_len = cfg.get('fixedLen', 0)  # 固定长度
    len_prefix = cfg.get('lenPrefix')  # 长度前缀
    remainder = cfg.get('remainder', False)  # 是否使用剩余字节模式
    padding_byte = _ensure_int(cfg.get('padding', 0x00), path)  # 填充字节，默认0x00，确保是整数
    
    if isinstance(v, str):
        if ascii_mode:
            # ASCII模式：编码字符串并右对齐填充
            try:
                data = v.encode('ascii')
            except UnicodeEncodeError:
                raise _err(path, 'BYTES ASCII编码失败')
        else:
            # HEX模式：解析十六进制字符串
            hx = v.strip().lower()
            if len(hx) % 2 != 0:
                raise _err(path, 'BYTES HEX长度必须为偶数')
            try:
                data = bytes.fromhex(hx)
            except Exception:
                raise _err(path, 'BYTES HEX非法')
        
        # 处理固定长度
        if fixed_len > 0:
            if len(data) > fixed_len:
                raise _err(path, f'BYTES 数据长度({len(data)})超过固定长度({fixed_len})')
            
            if ascii_mode:
                # ASCII模式：右对齐填充（前面补占位符）
                padding = bytes([padding_byte]) * (fixed_len - len(data))
                data = padding + data
            else:
                # HEX模式：左对齐填充（后面补0x00）
                data = data + b'\x00' * (fixed_len - len(data))
        elif len_prefix is not None:
            # 长度前缀模式：添加长度前缀
            return write_len_by_prefix(str(len_prefix), len(data)) + data
        elif remainder:
            # 剩余字节模式：直接返回原始字节，不添加长度前缀
            return data
        
        return data
        
    if isinstance(v, dict) and 'items' in v:
        try:
            data = bytes(int(x) & 0xFF for x in v['items'])
        except Exception:
            raise _err(path, 'BYTES items 非法')
        
        # 处理固定长度
        if fixed_len > 0:
            if len(data) > fixed_len:
                raise _err(path, f'BYTES 数据长度({len(data)})超过固定长度({fixed_len})')
            # 用0x00填充到固定长度
            data = data + b'\x00' * (fixed_len - len(data))
        elif len_prefix is not None:
            # 长度前缀模式：添加长度前缀
            return write_len_by_prefix(str(len_prefix), len(data)) + data
        elif remainder:
            # 剩余字节模式：直接返回原始字节，不添加长度前缀
            return data
        
        return data
        
    raise _err(path, 'BYTES 缺少 value 或格式不支持')


LOGGER = logging.getLogger('monitor')


def _log_bytes(path: List[str], start: int, data: bytes, note: str = "") -> None:
    if LOGGER.isEnabledFor(logging.DEBUG):
        rng = f"[{start}:{start + len(data) - 1}]" if data else "[-]"
        prefix = '/'.join(path)
        hex_str = to_hex(data)
        msg = f"{prefix} {rng} len={len(data)} {note} bytes={hex_str}"
        LOGGER.debug(msg)


def encode_field(field_cfg: Dict[str, Any], state: Dict[str, Any], path: List[str], base_offset: int = 0, **params) -> bytes:
    t = str(field_cfg.get('type', '')).upper()
    
    # 应用字段修改：mark标记和模拟器
    field_cfg = _apply_field_modifications(field_cfg, path, **params)
    
    if t == DataType.BYTE.value:
        return _encode_number(field_cfg.get('value'), 1, path)
    if t == DataType.WORD.value:
        return _encode_number(field_cfg.get('value'), 2, path)
    if t == DataType.DWORD.value:
        return _encode_number(field_cfg.get('value'), 4, path)
    if t == DataType.BCD.value:
        # 严格BCD：digits 表示字节数；value 为十进制字符串或数字
        val = field_cfg.get('value')
        digits = int(field_cfg.get('digits', 0))
        if digits <= 0:
            raise _err(path, 'BCD 需要 digits')
        if val is None:
            raise _err(path, 'BCD 缺少 value')
        s = str(val)
        # 如果是模拟器生成的时间字符串，提取纯数字
        if not s.isdigit():
            s = ''.join(c for c in s if c.isdigit())
        if not s.isdigit():
            raise _err(path, 'BCD 值必须为十进制字符串')
        # BCD编码：每个字节存储2位数字，所以最大数字位数 = digits * 2
        max_digits = digits * 2
        if len(s) > max_digits:
            raise _err(path, f'BCD 值长度超出 digits={digits}字节 (max_digits={max_digits}, actual={len(s)})')
        # 左补零到 digits*2 长度（确保偶数位）
        s = s.zfill(max_digits)
        # 复用 encode_time_bcd 的 direct 分支以保证一致的两位压缩逻辑
        return encode_time_bcd({'digits': max_digits, 'mode': 'direct', 'value': s})
    if t == DataType.BYTES.value:
        # 在MAP上下文中可选择抑制BYTES自身的长度前缀（仅保留外层MAP的valueLenPrefix）
        if params.get('suppress_string_lenprefix') and field_cfg.get('lenPrefix') is not None:
            local_cfg = dict(field_cfg)
            # 去掉内部lenPrefix，按原始字节输出（或固定长度/剩余策略由配置决定）
            # 若未声明fixedLen/remainder，则强制 remainder 模式以输出原始内容
            local_cfg.pop('lenPrefix', None)
            if local_cfg.get('fixedLen') is None and not local_cfg.get('remainder', False):
                local_cfg['remainder'] = True
            return _encode_bytes(local_cfg, path)
        return _encode_bytes(field_cfg, path)
    if t == DataType.BITFIELD.value:
        size = int(field_cfg.get('size', 32))
        if size not in [8, 16, 32, 64]:
            raise _err(path, f'BITFIELD size 必须是 8/16/32/64，实际: {size}')
        bits = field_cfg.get('bits', [])
        acc = 0
        for b in bits:
            idx = int(b['bitIndex'])
            bit_length = int(b.get('bitLength', 1))  # 默认长度为1
            val = int(b['value'])
            
            # 验证bit范围
            max_idx = size - 1
            if idx < 0 or idx > max_idx:
                raise _err(path, f'bitIndex 越界(0..{max_idx}): {idx}')
            if idx + bit_length - 1 > max_idx:
                raise _err(path, f'bit范围越界: bitIndex={idx}, bitLength={bit_length}, 最大位={max_idx}')
            if bit_length <= 0:
                raise _err(path, f'bitLength 必须大于0: {bit_length}')
            
            # 验证值范围
            max_val = (1 << bit_length) - 1
            if val < 0 or val > max_val:
                raise _err(path, f'bit值越界: value={val}, 最大值={max_val} (bitLength={bit_length})')
            
            # 将值写入对应的bit位置（大端序）
            acc |= (val << idx)
        
        mask = (1 << size) - 1
        acc &= mask
        if size == 8:
            return bytes([acc])
        elif size == 16:
            return write_uint(acc, 2)
        elif size == 32:
            return write_uint(acc, 4)
        elif size == 64:
            return write_uint(acc, 8)
    if t == DataType.STRING.value:
        # 在MAP上下文中可选择抑制字符串自身的长度前缀（仅保留外层MAP的valueLenPrefix）
        if params.get('suppress_string_lenprefix') and field_cfg.get('lenPrefix') is not None:
            local_cfg = dict(field_cfg)
            # 去掉内部lenPrefix，按原始字节输出（或固定长度/剩余策略由配置决定）
            # 若未声明fixedLen/remainder，则强制 remainder 模式以输出原始内容
            local_cfg.pop('lenPrefix', None)
            if local_cfg.get('fixedLen') is None and not local_cfg.get('remainder', False):
                local_cfg['remainder'] = True
            return _encode_string(local_cfg, path)
        return _encode_string(field_cfg, path)
    if t == DataType.STRUCT.value:
        fields = field_cfg.get('fields', [])
        if not isinstance(fields, list):
            raise _err(path, 'STRUCT 需要 fields 数组')
        if not fields:
            # 空消息体，返回空字节
            return b''
        # 若提供 value 且为对象，则按字段名将其映射为字段的值/局部配置覆盖
        provided_values = field_cfg.get('value')
        if isinstance(provided_values, dict) and provided_values:
            for f in fields:
                name = f.get('name')
                if name in provided_values:
                    v_override = provided_values[name]
                    if isinstance(v_override, dict):
                        # 特殊处理BITFIELD：如果是简化格式{"bit0": 1, "bit1": 0}，转换为bits数组
                        if f.get('type') == DataType.BITFIELD.value and any(k.startswith('bit') for k in v_override.keys()):
                            # 将简化格式转换为标准bits数组
                            bits = f.get('bits', [])
                            if bits:
                                # 更新bits数组中的值
                                for bit_def in bits:
                                    bit_idx = bit_def.get('bitIndex')
                                    bit_len = bit_def.get('bitLength', 1)
                                    # 生成字段名（与解码器保持一致）
                                    if bit_len == 1:
                                        key_name = f'bit{bit_idx}'  # 尝试简化格式 bit0, bit1
                                        key_name_underscore = f'bit_{bit_idx}'  # 尝试下划线格式 bit_0, bit_1
                                    else:
                                        key_name = f'bit{bit_idx}_{bit_idx + bit_len - 1}'
                                        key_name_underscore = f'bit_{bit_idx}_{bit_idx + bit_len - 1}'
                                    
                                    # 尝试匹配简化格式和下划线格式
                                    if key_name in v_override:
                                        bit_def['value'] = v_override[key_name]
                                    elif key_name_underscore in v_override:
                                        bit_def['value'] = v_override[key_name_underscore]
                        else:
                            # 非BITFIELD字段，直接合并配置
                            for k, vv in v_override.items():
                                f[k] = vv
                    else:
                        f['value'] = v_override
        # 按 order 升序
        try:
            fields_sorted = sorted(fields, key=lambda f: int(f['order']))
        except Exception:
            raise _err(path, 'STRUCT 字段需要整数 order')
        out = bytearray()
        # 用于条件字段求值的上下文
        context = {}
        for f in fields_sorted:
            name = f.get('name') or ''
            # 检查条件字段
            if 'conditional' in f:
                condition = f['conditional']
                # 简单的条件求值：支持 positionType != 0 这种形式
                if not _evaluate_condition(condition, context):
                    continue
            sub_cfg = f
            local_offset = len(out)
            field_bytes = encode_field(sub_cfg, state, path + [name], base_offset=base_offset + local_offset, **params)
            out += field_bytes
            _log_bytes(path + [name], base_offset + local_offset, field_bytes)
            # 更新上下文，用于后续字段的条件判断
            if f.get('type') == 'BYTE':
                context[name] = f.get('value', 0)
        return bytes(out)
    if t == DataType.ARRAY.value:
        # 在MAP上下文中可选择抑制ARRAY自身的长度前缀（仅保留外层MAP的valueLenPrefix）
        if params.get('suppress_string_lenprefix') and field_cfg.get('lenPrefix') is not None:
            local_cfg = dict(field_cfg)
            # 去掉内部lenPrefix，直接输出数组内容
            local_cfg.pop('lenPrefix', None)
            # 重新调用编码，但不带lenPrefix
            return encode_field(local_cfg, state, path, base_offset, **params)
        
        len_prefix = field_cfg.get('lenPrefix')
        elem = field_cfg.get('elem')
        items = field_cfg.get('items', [])
        count_by_field = field_cfg.get('countByField')
        
        # 允许省略 elem，直接通过 fields 提供元素结构模板
        if elem is None:
            if 'fields' in field_cfg:
                elem = {'type': DataType.STRUCT.value, 'fields': field_cfg['fields']}
            else:
                raise _err(path, 'ARRAY 需要 elem 或 fields 声明')
        if not isinstance(items, list):
            raise _err(path, 'ARRAY items 必须为数组')
        
        payload = bytearray()
        for idx, it in enumerate(items):
            # 将 elem 声明与值合并：elem 作为类型模板，it 作为 value 覆盖
            if isinstance(elem, dict):
                sub = dict(elem)
                sub['value'] = it if 'type' in elem and elem['type'] != DataType.STRUCT.value else it
            else:
                raise _err(path, 'ARRAY elem 必须为对象')
            local_offset = len(payload)
            child = encode_field(sub, state, path + [f'[{idx}]'], base_offset=base_offset + 1 + local_offset, **params)
            payload += child
        
        # 如果没有lenPrefix，直接返回数组内容（用于MAP上下文中的抑制模式）
        if not len_prefix:
            return bytes(payload)
        
        # 处理计数前缀
        if count_by_field:
            # 使用countByField进行动态分类计数
            field_name = count_by_field.get('field')
            counters = count_by_field.get('counters', [])
            
            if not field_name or not counters:
                raise _err(path, 'countByField 需要 field 和 counters')
            
            # 统计各个分类的数量
            count_values = {}
            for counter in counters:
                counter_name = counter.get('name')
                condition = counter.get('condition')
                count_values[counter_name] = 0
                
                if not condition:
                    raise _err(path, f'counter {counter_name} 需要 condition')
                
                # 遍历items统计匹配的数量
                for item in items:
                    if isinstance(item, dict) and field_name in item:
                        if _evaluate_condition_expression(item[field_name], condition):
                            count_values[counter_name] += 1
            
            # 写入各个计数值
            prefix_data = bytearray()
            for counter in counters:
                counter_name = counter.get('name')
                counter_type = counter.get('type', 'BYTE')
                count_val = count_values.get(counter_name, 0)
                counter_bytes = _encode_number(count_val, 1 if counter_type == 'BYTE' else 2 if counter_type == 'WORD' else 4, path + [counter_name])
                prefix_data += counter_bytes
                _log_bytes(path + [counter_name], base_offset + len(prefix_data) - len(counter_bytes), counter_bytes, note='countByField')
        else:
            # 使用原有的总数计数
            prefix_data = write_len_by_prefix(str(len_prefix), len(items))
            _log_bytes(path + ['len'], base_offset, prefix_data, note='lenPrefix')
        
        data = bytes(prefix_data) + bytes(payload)
        _log_bytes(path, base_offset + len(prefix_data), bytes(payload), note='arrayPayload')
        return data
    if t == DataType.MAP.value:
        # 仅支持数字型 key；key 占用字节数通过 keyFixedLen 指定
        key_len = int(field_cfg.get('keyFixedLen', 0))
        val_len_prefix = field_cfg.get('valueLenPrefix')
        entries = field_cfg.get('entries', [])
        count_prefix = field_cfg.get('countPrefix')  # 新增：数量字段前缀
        count_len = int(field_cfg.get('countLen', 0))  # 新增：数量字段长度
        
        if key_len <= 0:
            raise _err(path, 'MAP 需要 keyFixedLen > 0')
        if not val_len_prefix:
            raise _err(path, 'MAP 需要 valueLenPrefix')
        
        # 计算启用的条目数量
        enabled_entries = [ent for ent in entries if ent.get('enabled', True)]
        entry_count = len(enabled_entries)
        
        out = bytearray()
        
        # 如果配置了数量字段且长度大于0，先写入数量
        if count_len > 0:
            # 根据countLen隐式确定前缀类型
            if count_len == 1:
                count_bytes = write_len_by_prefix("BYTE", entry_count)
            elif count_len == 2:
                count_bytes = write_len_by_prefix("WORD", entry_count)
            elif count_len == 4:
                count_bytes = write_len_by_prefix("DWORD", entry_count)
            else:
                raise ValueError(f"不支持的countLen值: {count_len}")
            out += count_bytes
            _log_bytes(path + ['count'], base_offset, count_bytes, note='countPrefix')
        for i, ent in enumerate(entries):
            # 支持通过 enabled 控制是否编码
            if ent.get('enabled') is False:
                continue
            if 'key' not in ent:
                raise _err(path, f'MAP 条目缺少 key (index={i})')
            # 对于BITFIELD、STRUCT类型，或者有模拟器配置的条目，不需要value字段
            has_simulator = ent.get('simulator', {}).get('enable', False)
            if 'value' not in ent and not has_simulator and ent.get('type', '').upper() not in ['BITFIELD', 'STRUCT']:
                raise _err(path, f'MAP 条目缺少 value (index={i})')
            # 统一使用 type 字段
            if 'type' not in ent:
                raise _err(path, f'MAP 条目缺少 type (index={i})')
            value_type = ent['type']
            key_iv = _ensure_int(ent['key'], path + [f'key#{i}'])
            # 写 key（定长，大端）
            key_bytes = _encode_number(key_iv, key_len, path + [f'key#{i}'])
            key_offset = len(out)
            out += key_bytes
            # 编码 value
            # 处理新的简化格式
            if isinstance(value_type, dict):
                v_cfg = dict(value_type)
            else:
                v_cfg = {'type': value_type}
                # 如果条目本身有fields属性（新格式），则合并到v_cfg中
                if 'fields' in ent:
                    v_cfg['fields'] = ent['fields']
                # 合并其他属性（如digits, fixedLen等，以及simulator）
                for attr in ['digits', 'fixedLen', 'lenPrefix', 'elem', 'bits', 'simulator']:
                    if attr in ent:
                        v_cfg[attr] = ent[attr]
            vt = str(v_cfg.get('type', '')).upper()
            # 对于BITFIELD类型，值通过bits数组定义
            if vt == DataType.BITFIELD.value:
                val = ent.get('bits', [])
                # 为BITFIELD构建正确的配置
                v_cfg = {
                    'type': DataType.BITFIELD.value,
                    'size': ent.get('size', 32),
                    'bits': val
                }
            elif vt == DataType.STRUCT.value:
                # STRUCT类型，如果没有value字段，使用空字典
                val = ent.get('value', {})
            else:
                # 如果有模拟器，value可以为None（模拟器会生成）
                val = ent.get('value', None)
            if vt == DataType.STRUCT.value:
                # 合并策略：
                # - 若 value 含 fields，则完全替换 schema（自定义结构）
                # - 否则按字段名将 value 映射为字段的 value 覆盖；空对象则沿用 schema 默认
                if isinstance(val, dict) and 'fields' in val:
                    v_cfg = dict(val)
                    v_cfg['type'] = DataType.STRUCT.value
                else:
                    fields = v_cfg.get('fields', [])
                    if not isinstance(fields, list) or not fields:
                        raise _err(path + [f'value#{i}'], 'STRUCT 缺少 fields')
                    if isinstance(val, dict) and val:
                        for f in fields:
                            name = f.get('name')
                            if name in val:
                                v_override = val[name]
                                if isinstance(v_override, dict):
                                    # 允许覆盖该字段的局部配置
                                    for k, vv in v_override.items():
                                        f[k] = vv
                                else:
                                    f['value'] = v_override
                    v_cfg['fields'] = fields
            else:
                v_cfg['value'] = val
            v_offset = len(out)
            # 在MAP的值编码时，只对直接作为MAP值的长度类型字段抑制长度前缀
            # 需要抑制的类型：STRING、BYTES、ARRAY
            # 不需要抑制的类型：STRUCT（因为MAP的valueLenPrefix和STRUCT内部字段的lenPrefix服务于不同目的）
            # BCD类型使用digits而不是lenPrefix，所以不需要抑制
            suppress_lenprefix = vt in [DataType.STRING.value, DataType.BYTES.value, DataType.ARRAY.value]
            v_bytes = encode_field(v_cfg, state, path + [f'value#{i}'], base_offset=base_offset + v_offset, suppress_string_lenprefix=suppress_lenprefix, **params)
            # 写入 ValueLength
            len_bytes = write_len_by_prefix(str(val_len_prefix), len(v_bytes))
            out += len_bytes
            # 写入 Value
            out += v_bytes
            _log_bytes(path + [f'key#{i}'], base_offset + key_offset, key_bytes)
            _log_bytes(path + [f'value#{i}', 'len'], base_offset + v_offset, len_bytes, note='lenPrefix')
            _log_bytes(path + [f'value#{i}'], base_offset + v_offset + len_bytes.__len__(), v_bytes)
        return bytes(out)
    raise _err(path, f"未支持的类型: {t}")


